/*
 * greatoutdoors.c
 */

#include <agent/player.h>
#include <agent/go_unit.h>
#include "rowgage.h"
#include <controller/action.h>
#include <controller/controller.h>
#include <controller/event.h>
#include <controller/listener.h>
#include <controller/timer.h>
#include <gamelogic/action_utils.h>
#include <gamelogic/event_utils.h>
#include <gamelogic/npc.h>
#include <gamelogic/npc_lua.h>
#include <gameview/intro.h>
#include <lua/luaRcallback.h>
#include <stdio.h>
#include <unistd.h>

static unit *avatar;
static go_stats avatar_stats;

/*
 * Test handlers
 */
void movement_handler(event* e) {
    move_event *me;
    me = (move_event*)e->info;
    /* Simple logger to test listeners */
    /*printf("%s moved %ld, %ld.\n", me->mover->name, me->delta_x, me->delta_y);*/
}

void damage_handler(event* e) {
    damage_event *de;
    de = (damage_event*)e->info;
    /* Simple logger to test listeners */
    printf("%s was damaged for %u health!\n", de->victim->name, de->amount);
}

void bleed_handler(event* e) {
    bleed_event *be;
    be = (bleed_event*)e->info;
    /* Simple logger to test listeners */
    printf("%s is bleeding, losing %u health!\n", be->victim->name, be->amount);
}

void attack_handler(event* e) {
    attack_event *ae;
    ae = (attack_event*)e->info;
    /* Simple logger to test listeners */
    printf("%s has attacked %s, dealing %u damage!\n",
        ae->attacker->name, ae->victim->name, ae->amount);
}

/*
 * Moves the given unit by a given delta x/y.
 */
void npc_move_unit( unit *u, long delta_x, long delta_y ) {
    action *new_action;
    new_action = create_move_action(move_event_t, u, delta_x, delta_y);
    controller_add(new_action);
}

/*
 * Damages the given unit by a given amount.
 */
void npc_damage_unit( unit *u, int amount ) {
    action *new_action;
    new_action = create_damage_action(damage_event_t, u, amount);
    controller_add(new_action);
}

/*
 * Attacks the second given unit with the first for a given amount.
 */
void npc_attack_unit( unit *a, unit *v, int amount ) {
    action *new_action;
    new_action = create_attack_action(attack_event_t, a, v, amount);
    controller_add(new_action);
}

/*
 * Lua hooks
 */
void go_lua_move( int argc, struct lua_arg_list *argv ) {
    unit *u;
    int x;
    int y;
    if( argc < 3 ) {
        printf("Too few arguments in go_lua_move call\n");
    }
    else {
        argv = luaR_arg_getinteger(argv, &x);
        u = npc_get(x);
        argv = luaR_arg_getinteger(argv, &x);
        argv = luaR_arg_getinteger(argv, &y);
        npc_move_unit(u, x, y);
    }
}

void go_lua_damage( int argc, struct lua_arg_list *argv ) {
    unit *u;
    int amount;
    if( argc < 2 ) {
        printf("Too few arguments in go_lua_damage call\n");
    }
    else {
        argv = luaR_arg_getinteger(argv, &amount);
        u = npc_get(amount);
        argv = luaR_arg_getinteger(argv, &amount);
        npc_damage_unit(u, amount);
    }
}

void go_lua_attack( int argc, struct lua_arg_list *argv ) {
    unit *a;
    unit *v;
    int amount;
    int index;
    if( argc < 3 ) {
        printf("Too few arguments in go_lua_attack call\n");
    }
    else {
        argv = luaR_arg_getinteger(argv, &index);
        if( index == -1 ) {
            a = player_get();
        }
        else if( index >= 0 && index <= npc_last_id() ) {
            a = npc_get(index);
        }
        else {
            printf("Nonexistant NPC referenced in go_lua_attack call\n");
            return;
        }
        argv = luaR_arg_getinteger(argv, &index);
        if( index == -1 ) {
            v = player_get();
        }
        else if( index >= 0 && index <= npc_last_id() ) {
            v = npc_get(index);
        }
        else {
            printf("Nonexistant NPC referenced in go_lua_attack call\n");
            return;
        }
        argv = luaR_arg_getinteger(argv, &amount);
        npc_attack_unit(a, v, amount);
    }
}

void * make_go_stats(int argc, struct lua_arg_list *argv, unit *u ) {
    go_stats *stats;
    int size;
    int hp;
    if( argc < 2 ) {
        /* TODO: error */
        return NULL;
    }
    else {
        argv = luaR_arg_getinteger(argv, &size);
        argv = luaR_arg_getinteger(argv, &hp);
        stats = (go_stats*)malloc(sizeof(go_stats));
        stats->hp = hp;
        stats->hp_max = hp;
        stats->size = size;
        stats->u = u;
        return (void*)stats;
    }
}

void push_go_stats( void *p ) {
    go_stats *stats;
    stats = (go_stats*)p;
    if( p != NULL ) {
        npc_lua_pushnumber("hp", stats->hp);
        npc_lua_pushnumber("hp_max", stats->hp_max);
        npc_lua_pushnumber("size", stats->size);
    }
}

void greatoutdoors_move_unit( long delta_x, long delta_y ) {
    if( ((go_stats*)avatar->stats)->hp > 0 ) {
        npc_move_unit(avatar, delta_x, delta_y);
    }
}

void greatoutdoors_damage_unit( int amount ) {
    if( ((go_stats*)avatar->stats)->hp > 0 ) {
        npc_damage_unit(avatar, amount);
    }
}

void greatoutdoors_next_event() {
    controller_next();
}

unsigned long greatoutdoors_player_x() {
    return avatar->x;
}

unsigned long greatoutdoors_player_y() {
    return avatar->y;
}

/*
 * The keyboard callback. Grabs whatever key was pressed and converts it into
 * a char we can test against and act accordingly.
 */
void keyboard( unsigned char key, int x, int y ) {
    switch(key) {
        case 'w': greatoutdoors_move_unit(-10l, 10l); break;
        case 'e': greatoutdoors_move_unit(0l, 10l); break;
        case 'r': greatoutdoors_move_unit(10l, 10l); break;
        case 'f': greatoutdoors_move_unit(10l, 0l); break;
        case 'v': greatoutdoors_move_unit(10l, -10l); break;
        case 'c': greatoutdoors_move_unit(0l, -10l); break;
        case 'x': greatoutdoors_move_unit(-10l, -10l); break;
        case 's': greatoutdoors_move_unit(-10l, 0l); break;
        case 'd': greatoutdoors_damage_unit(20); break;
        case 'q': exit(0); /* TODO: This should probably exit some other way */
    }
}

int greatoutdoors_init() {
    /* Register our events */
    move_event_t = event_register(&move_unit);
    damage_event_t = event_register(&damage_unit);
    bleed_event_t = event_register(&bleed_unit);
    attack_event_t = event_register(&attack_unit);

    /* Register our listeners */
    listener_register(move_event_t, &movement_handler);
    listener_register(damage_event_t, &damage_handler);
    listener_register(bleed_event_t, &bleed_handler);
    listener_register(attack_event_t, &attack_handler);

    /* Create our avatar */
    avatar_stats.size = 10;
    avatar_stats.hp = 100;
    avatar_stats.hp_max = 100;
    avatar = player_create(350, 350, 0, "Player", &avatar_stats);
    avatar_stats.u = avatar;

    /*rgSetKeyboardCallback(keyboard);*/
    luaR_callback_register("RG_npc", "move", go_lua_move);
    luaR_callback_register("RG_npc", "damage", go_lua_damage);
    luaR_callback_register("RG_npc", "attack", go_lua_attack);
    intro_enable(keyboard);
    return 0;
}

int main( int argc, char** argv ) {
    npc_lua_stats(make_go_stats); /* TODO: put this with rest of go init */
    npc_lua_push_callback(push_go_stats); /* TODO: put this with the rest of init */
    rgInit(greatoutdoors_init, &argc, argv);
    return 0;
}
